import React from 'react';
import 'babel-polyfill';
// require( 'string.prototype.at');
import {
    Text,
    Button,
    View,
    ScrollView,
} from 'react-native';

export default class ES6TestScreen extends React.Component {

    static navigationOptions = {
        title: '测试es6',
    };

    TAG = "测试----->>";

    constructor(props) {
        super(props);
        this.letTest = this.letTest.bind(this);
        this.destructionTest = this.destructionTest.bind(this);
        this.stringTest = this.stringTest.bind(this);
        this.regularTest = this.regularTest.bind(this);
        this.numberTest = this.numberTest.bind(this);
        this.funcTest = this.funcTest.bind(this);
        this.arrayTest = this.arrayTest.bind(this);
        this.objectTest=this.objectTest.bind(this);
    };

    /**
     * let var 和const
     */
    letTest() {

        /*        //test1:************************************************************************
                {
                    let a = 10;
                    var b = 1;
                    // console.log(this.TAG+a);
                }
                // console.log(this.TAG+a);//let命令所在的代码块内有效。
                // console.log(this.TAG+b);//var相当于全局变量

                //test2:************************************************************************
                var a=[];
                for(var i=0;i<10;i++){
                    a[i]=function () {
                        console.log(i);
                    };
                }
                a[6]();//let 输出：6 var 输出：10

                //test3:************************************************************************
                for(let i=0;i<3;i++){
                    let i='abc';
                    console.log(i)
                }
                //输出3个abc 说明循环变量i和循环体内的i不是同一个作用域内的。

                //test4:************************************************************************
                console.log(foo);//undefined
                var foo=2;
                console.log(bar);//undefined
                let bar=2;
                //和阮一峰说的不一样，最好不要这样用。

                //test5:************************************************************************
                var tmp=123;
                if(true){
                    console.log(tmp);//undefined
                    tmp='abc';
                    console.log(tmp);//abc
                    let tmp;
                    console.log(tmp);//undefined
                }
                //和阮说的不一样，难道我用的不是es6？？？
                //反正最好先声明，再使用，不纠结

                //test6:************************************************************************
                var date=new Date();
                function f() {
                    console.log(date);
                    if(false){
                        var date = 'hello world';
                    }
                }
                f();//undefined


        //test7:************************************************************************
        //块级作用域
        //声明函数f
        function f() {
            console.log('I am outside');
        }

        (
            function () {
                if (true) {
                    //重复声明函数f
                    function f() {
                        console.log('i am inside');
                    }

                    f();// I am inside 块级作用域里面上面，只能在块级作用域里面调用
                }
                //调用函数
                f();// I am outside
            }()
        );

        //test8:************************************************************************
        // function getT() {
        //     return 2;
        // }
        //
        // let x = do {
        //     let t = getT();
        //     t * t + 1;
        // }
        // console.log(x);

                */
        //test9:************************************************************************
        //const实际上保证的，并不是变量的值不得改动，而是变量指向的那个内存地址不得改动。
        const foo = {};
        foo.prop = 123;
        foo.name = 'jiang';
        foo.name = 'hu';
        console.log(foo.name);//hu
        // foo={}; //TransformError  "foo" is read-only

        const m = 10;
        // m=20;
    }

    /**
     * 变量的解构赋值
     */
    destructionTest() {

        /*      //基本用法*******************************************************************
              let [a, b, c] = [1, 2, 3];
              console.log(a);//注意左边是数组，右边也要是数组
              let [head, ...tail] = [1, 2, 3, 4];//注意这种形式的...tail要放在最后面，且只能有一个
              console.log(tail);//[ 2, 3, 4 ]
              let [x, y, z] = new Set(['a', 'b', 'c']);
              console.log(x);//a 只要某种数据结构具有 Iterator 接口，都可以采用数组形式的解构赋值。
              //todo Generator 例子后面看Generator语法再看

              //默认值**********************************************************************
              let [foo = true] = [];
              console.log(foo);

              // let [x1,y1='b']=['a'];
              let [x1,y1='b']=['a',undefined];
              // let [x1,y1='b']=['a',null];
              console.log(x1);
              console.log(y1);//b,b,null

              function getaaa() {
                  console.log('aaa');
              }
              // let [aaa=getaaa()]=[1];
              // console.log(aaa);//1，没有执行getaaa（）方法,aaa直接赋值为1
              let [aaa=getaaa()]=[];//aaa,执行getaaa（）方法


        //对象的解构赋值***********************************************************
        //对象的属性没有次序，变量必须与属性同名，才能取到正确的值。
        //数组的值用[],对象的值用{}
        let {foo, bar} = {foo: "aaa", bar: 'bbb'};
        console.log(foo + "," + bar);


        // // 如果变量名和属性名不一致
        // let obj = {first: 'hello', last: 'world'};
        // console.log(obj.first + "," + obj.last);//first last 是属性名(阮说是模式)
        // let {first: f, last: l} = obj;//f，l是变量
        // // let {first:f,last:l}={first:'hello',last:'worlld'}; //也可以直接这样写
        // //找到同为first属性的变量和值，把值hello赋值给变量f
        // console.log(f + "," + l);

        // 如果要把first last变成变量，可以这么做
        let {first: first, last: last} = {first: 'hello', last: 'worlld'};
        //属性名和变量名一样的可以省略成：
        //let {first,last}={first:'hello',last:'worlld'};
        console.log(first);
        console.log(last);


        // //解构赋值嵌套
        // let myObj = {p: ['hello', {y: 'world'}]};
        // //myObj：对象，
        // //p:属性 是个数组，第一个值是hello，第二个值是y属性，值为world
        // let {p: [x, {y}]} = myObj;
        // //p:匹配p x赋值为hello，y赋值world
        // console.log(x);
        // console.log(y);

        //现在p是模式，如果p作为变量,改为：
        let {p, p: [x, {y}]} = {p: ['hello', {y: 'world'}]};
        console.log(p);


        //第二个例子
        let node = {
            loc:{
                start:{
                    line:1,
                    column:5
                }
            }
        };
        let {loc,loc:{start},loc:{start:{line}}}=node;
        console.log(line);//1
        console.log(start);// { line: 1, column: 5 }
        console.log(loc);// { start: { line: 1, column: 5 } }
        //上面代码有三次解构赋值，分别是对loc、start、line三个属性的解构赋值
        // 注意，最后一次对line属性的解构赋值之中，只有line是变量，loc和start都是模式，不是变量。

        let obj1={};
        let arr1=[];
        //todo 为什么要（）？？？？？？
        ({foo:obj1.prop,bar:arr1[0]}={foo:123,bar:true});
        console.log(obj1.prop);
        console.log(arr1);


        //指定默认值
        let {x=3}={};
        console.log(x);//3
        let {m,n=5}={m:1};
        console.log(m);//1
        let {i:j=5}={i:10};
        console.log(j);//10

        let y;
        ({y}={y:1});//加（）是为了防止js引擎将{x}理解成代码块。

        //数组本质是特殊的对象，因此可以对数组进行对象属性的解构。
        let arr=[4,5,6];
        let {0:first,[arr.length-1]: last}=arr;
        console.log(first);//4
        console.log(last);//6


        //字符串的解构赋值***********************************************************
        //todo 报错了
        // let [a,b,c,d,e]='hello';
        // console.log(a);
        // console.log(b);
        // console.log(c);
        // console.log(d);
        // console.log(e);

        let {length: len} = 'hello';
        console.log(len);

        //数值和布尔值的解构赋值***********************************************************
        let {toString: s} = 123;
        console.log(s);//[Function: toString]
        console.log(s === Number.prototype.toString);//true
        //如果等号右边是数值和布尔值，则会先转为对象。
        // 数值和布尔值的包装对象都有toString属性，因此变量s都能取到值。

        // let { prop: x } = undefined; // TypeError
        // let { prop: y } = null; // TypeError

        //函数参数的解构赋值***********************************************************
        //，函数add的参数表面上是一个数组，但在传入参数的那一刻，数组参数就被解构成变量x和y。
        // 对于函数内部的代码来说，它们能感受到的参数就是x和y。
        function add([x, y]) {
            return x + y;
        }

        var add = add([1, 2]);
        console.log(add);

        var map = [[1, 2], [3, 4]].map(([a, b]) => a + b);
        console.log(map);//[ 3, 7 ]

        //函数move的参数是一个对象，通过对这个对象进行解构，
        // 得到变量x和y的值。如果解构失败，x和y等于默认值。
        function move({x = 0, y = 0} = {}) {
            return [x, y];
        }

        var move2 = move({x: 3, y: 5});
        console.log(move2);// [ 3, 5 ]

        //圆括号的问题：
        //不可用：
        // 1.变量声明语句，
        // 2.函数参数（也是声明语句）
        // 3.赋值语句的模式部分,（个人觉得模式部分也相当于声明）
        //可用：
        //赋值语句的非模式部分
        [(b)] = [3]; // 正确  模式是取数组的第一个成员
        ({ p: (d) } = {}); // 正确 模式是p，而不是d
        [(parseInt.prop)] = [3]; // 正确
  */
        //用途*****************************************************************************
        //交换变量的值
        let x = 1;
        let y = 2;
        [x, y] = [y, x];
        console.log(x, y);//   2,1
        //从函数返回多个值
        function fun1() {
            return [1, 2, 3];
        }

        let [a, b, c] = fun1();
        console.log(a, b, c);

        function fun2() {
            return {
                foo: 1,
                bar: 2,
            };
        }

        let {foo, bar} = fun2();
        console.log(foo, bar);

        //函数参数的定义
        function fun3([x, y, z]) {
            return x + y + z;
        }

        console.log(fun3([1, 2, 3]));

        function fun4({x, y, z}) {
            return x * y * z;
        }

        console.log(fun4({x: 3, y: 4, z: 5}));//对象的形式不需要管参数的顺序
        //提取json数据  快速提取 JSON 数据的值。
        let jsonData = {
            id: 100,
            status: "ok",
            data: [123, 456],
        };
        let {id: goodsId, status, data} = jsonData;
        console.log(goodsId, status, data);
        //函数参数的默认值
        let fun5 = function (url, {
            async = true,
            beforeSend = function () {
            },
            cache = true,
            complete = function () {
            },
            crossDomain = false,
            global = true,
        }) {

        };
        //遍历map结构
        var map = new Map();
        map.set('first', 'word');
        map.set('second', 'world');

        for (let [key, value]of map) {
            console.log(key + "is" + value);
        }

    }

    /**
     * 字符串扩展
     */
    stringTest() {
        //字符的unicode
        console.log('\z');
        // console.log('\172');//122 八进制 严格模式不可使用八进制
        console.log('\x7a');//122 十六进制
        console.log('\u007a');
        console.log('\u{7a}');

        //******************************************************************************************
        //js内部存储，字符以utf-16的格式存储，每个字符固定为2个字节，对于需要4个字节的字符（Unicode的码点大于0xffff）的字符
        //js会认为它们是两个字符
        //es6中使用codePointAt方法可以正确处理4个字节存储的字符
        var s = '𠮷';//码点：0x20BB7   UTF-16编码为0xD842 0xDFB7（十进制为55362 57271）
        console.log(s.length);// 2 对于四个字节的，js不能正确处理，会认为字符串长度是2
        console.log(s.charAt(0));//� charAt 不能正确读取
        console.log(s.charCodeAt(0));//
        console.log(s.charCodeAt(1));//charCodeAt方法只能分别返回前两个字节和后两个字节的值
        console.log(s.codePointAt(0));//codePointAt 可以正确处理四字节的字符  十六进制码点0x20BB7  转化为十进制码点134071
        console.log(s.codePointAt(0).toString(16));//20bb7

        var s2 = String.fromCharCode(0x20BB7);//将码点转化为字符，不能转化大于0xffff的
        console.log(s2);//ஷ

        var s3 = String.fromCodePoint(0x20BB7);//es6中将码点转化为字符，只能转化大于0xffff的
        console.log(s3);//𠮷

        //******************************************************************************************

        //字符串的遍历接口
        //step1:npm install --save-dev babel-polyfill
        //step2: import 'babel-polyfill';
        //ES6为字符串添加了遍历器接口  for...of循环 相较于传统for循环，for,,,of可以识别大于0xffff的码点
        for (let codePoint of "foo") {
            console.log(codePoint);
        }

        console.log('abcd'.at(0));//，返回字符串给定位置的字符。该方法不能识别码点大于0xFFFF的字符。
        console.log('𠮷'.at(0));//可以识别 Unicode 编号大于0xFFFF的字符，返回正确的字符。
        //******************************************************************************************
        var s4 = 'hello word!';
        console.log(s4.startsWith('hello'));
        console.log(s4.includes('!'));
        console.log(s4.includes('o'));
        //三个方法都支持第二个参数，表示开始搜索的位置。
        console.log(s4.startsWith('word', 6));//从w的位置开始搜索的
        console.log(s4.includes('word', 6));//从w的位置开始搜索的
        console.log(s4.endsWith('word', 10));//从d的位置开始搜索的
        //******************************************************************************************
        //repeat（n）返回重复n次的字符串
        console.log('ha'.repeat(3));//hahaha
        console.log('ha'.repeat(2.9));//haha  取整数次
        console.log('ha'.repeat(0));//""
        console.log('ha'.repeat(NaN));//""
        console.log('ha'.repeat('3'));//hahaha  参数是字符串，则会先转换成数字。

        //第一个参数用来指定字符串的最小长度，第二个参数是用来补全的字符串。
        console.log('12'.padStart(10, 'YYYY-MM-DD'));
        console.log('09-11'.padStart(10, 'YYYY-MM-DD'));

        //******************************************************************************************
        //模板字符串
        console.log(`In JavaScript
         is a line-feed.`);
        //如果在模板字符串中需要使用反引号，则前面要用反斜杠转义。
        console.log(`\`yo\`world!`);
        //模板字符串中嵌入变量，需要将变量名写在${}之中。
        var name = "Bob", time = "today";
        console.log(`hello ${name},how are you ${time}?`);
        //摸板字符串${}大括号内部可以放入任意的JavaScript表达式，
        // 可以进行运算，以及引用对象属性。
        let x = 1;
        let y = 2;
        console.log(`${x}+${y}=${x + y}`);// 1+2=3

        let obj = {x: 1, y: 2};
        console.log(`${obj.x + obj.y}`);

        //模板字符串之中还能调用函数。
        function fun1() {
            return "hello world";
        }

        console.log(`模板字符串调用函数${fun1()}`);

        // 写法一
        //  let str = 'return ' + '`Hello ${name}!`';
        //  let func = new Function('name', str);
        // // func('Jack') // "Hello Jack!"
        //  console.log(func("jack"));//todo Invalid character: '`' 报错


        // 写法二
        //  let str = '(name) => `Hello ${name}!`';
        //  let func = eval.call(null, str);
        //  // func('Jack') // "Hello Jack!"
        //  console.log(func('Jack')); //todo Unexpected token '>'报错

        //******************************************************************************************

        //todo 模板编译 没看

        //******************************************************************************************
        //标签模板
        //标签（也就是函数名）后面跟着一个模板字符串（也就是参数），
        // alert(123);
        // alert`123`;


        function tag(stringArr, ...values) {
            console.log(stringArr[0]);
            console.log(stringArr[1]);
            console.log(stringArr[2]);
            console.log(values[0]);
            console.log(values[1]);
        }

        var a = 5;
        var b = 10;
        //tag函数第一个参数是数组，该数组的成员是模板字符串中那些没有变量替换的部分
        //tag函数的其他参数，都是模板字符串各个变量被替换后的值
        tag`Hello  ${ a }world ${ b } `;
// 等同于
        console.log("等同于------------------");
        tag(['Hello ', 'world ', ' '], a, b);


        let total = 30;
        let msg = passthru`the total is ${total}( ${total * 1.05} with tax)`;

        function passthru(literals) {
            let result = '';
            let i = 0;
            console.log('literals', literals.length);
            console.log('arguments', arguments.length);
            while (i < literals.length) {
                result += literals[i++];
                if (i < arguments.length) {//arguments 参数数组
                    result += arguments[i];
                }
            }
            return result;
        }

        console.log(msg);
    }

    /**
     * 正则扩展
     * todo 这一部分需要时再看
     */
    regularTest() {
        var s = "hello".replace("l", "d");
        console.log(s);//hedlo
        var number = "hello".search("l");
        console.log(number);// 2
        var split = "hello,world".split(",");
        console.log(split[0], split[1]);// 'hello', 'world'
    }

    /**
     * 数值的扩展
     */
    numberTest() {
        //二进制0b，八进制0o
        console.log(0b11 === 3);//true
        console.log(0o11 === 9);//true
        console.log(Number('0b111'));//7

        // Number.isFinite()对于非数值一律返回false,
        // Number.isNaN()只有对于NaN才返回true，非NaN一律返回false。

        //Number.isFinite()用来检查一个数值是否为有限的（finite）。
        Number.isFinite(15); // true
        Number.isFinite(0.8); // true
        Number.isFinite(NaN); // false
        Number.isFinite(Infinity); // false
        Number.isFinite(-Infinity); // false
        Number.isFinite('foo'); // false
        Number.isFinite('15'); // false
        Number.isFinite(true); // false

        // Number.isNaN()用来检查一个值是否为NaN。
        Number.isNaN(NaN) // true
        Number.isNaN(15) // false
        Number.isNaN('15') // false
        Number.isNaN(true) // false
        Number.isNaN(9 / NaN) // true
        Number.isNaN('true' / 0) // true
        Number.isNaN('true' / 'true') // true


        //es6的写法
        console.log(Number.parseInt("12.34"));//12
        console.log(Number.parseFloat("12.34"));//12.34

        console.log(Number.isInteger(25.0), //js内，整数和浮点数是同样的存储方法 所以25和25.0被视为同一值
            Number.isInteger("12"),
            Number.isInteger(true));//true, false, false

        console.log(Number.EPSILON);//误差检查

        //Math对象的扩展
        //trunc 取整数部分，内部先转化为数值
        console.log(Math.trunc(4.2)
            , Math.trunc(-4.2)
            , Math.trunc(NaN)
            , Math.trunc("12")
            , Math.trunc()
        );//4, -4, NaN, 12, NaN
        //sign 判断一个数是正数(返回1)，负数（返回-1），还是0（不论是0还是-0都返回0）
        console.log(Math.sign(-5)
            , Math.sign(5)
            , Math.sign(0)
            , Math.sign(-0)
            , Math.sign(1)
            , Math.sign(-1)
        );
        //todo 指数对数等
    }

    /**
     * 函数的扩展
     */
    funcTest() {

        //es6允许为函数的参数设置默认值
        function log(x, y = 'word') {
            console.log(x, y);
        }

        log("hello");// 'hello', 'word'
        log("hello", "china");//'hello', 'china'
        log("hello", "");// 'hello', ''
        log("", "hello");//'', 'hello'

        //注意：每次调用函数，都会重新计算参数
        let x = 99;

        function foo(p = x + 1) {
            console.log(p);
        }

        foo() // 100
        x = 100;
        foo() // 101
        //*********************************************************************************************************//
        //结合解构函数
        function foo1({x, y = 5}) {
            console.log(x, y);
        }

        foo1({});//undefined, 5
        foo1({x: 1});//1,5
        foo1({x: 1, y: 2});//1,2
        // foo1();//undefined is not an object (evaluating '_ref10.x')

        //*********************************************************************************************************//
        //解构赋值默认值
        //函数参数的默认值是空对象，但是设置了对象解构赋值的默认值
        function fetch(url, {method = 'GET', body = '', heads = {writeOutTime: 1000, readOutTime: 1000}} = {}) {
            console.log(method, body, heads);
        }

        fetch('http://example.com');
        fetch('http://example.com', {method: 'POST', body: 'myBody', heads: {writeOutTime: 2000, readOutTime: 3000}});

        //----------------------------------------------------------------------------------------
        //写法一函数参数的默认值是空对象，但是设置了对象解构赋值的默认值；
        //写法二函数参数的默认值是一个有具体属性的对象，但是没有设置对象解构赋值的默认值。
        // 写法一
        function m1({x = 0, y = 0} = {}) {
            console.log([x, y]);
        }

        // 写法二
        function m2({x, y} = {x: 0, y: 0}) {
            console.log([x, y]);
        }

        // 函数没有参数的情况
        m1() // [0, 0]
        m2() // [0, 0]

        // x和y都有值的情况
        m1({x: 3, y: 8}) // [3, 8]
        m2({x: 3, y: 8}) // [3, 8]

        // x有值，y无值的情况
        m1({x: 3}) // [3, 0]
        m2({x: 3}) // [3, undefined]

        // x和y都无值的情况
        m1({}) // [0, 0];
        m2({}) // [undefined, undefined]

        m1({z: 3}) // [0, 0]
        m2({z: 3}) // [undefined, undefined]

        //定义了默认值的参数，应该是函数的尾参数,如果非尾部的参数设置默认值，实际上这个参数是没法省略的。

        //指定了默认值以后，函数的length属性，将返回没有指定默认值的参数个数

        //----------------------------------------------------------------------------------------
        //利用参数默认值，可以指定某一个参数不得省略，如果省略就抛出一个错误。
        //可以用undefined设置可以忽略的参数
        function throwIfMissing() {
            console.log('Missing parameter');
        }

        function foo2(mustBeProvided = undefined) {
            return mustBeProvided;
        }

        foo2(new throwIfMissing());
        // foo2();

        //*********************************************************************************************************//
        //es6 引入rest参数（形式为...变量名），用于获取函数的多余参数，这样就不需要使用arguments对象了
        // rest 参数搭配的变量是一个数组，该变量将多余的参数放入数组中
        // rest 参数之后不能再有其他参数（即只能是最后一个参数），否则会报错。
        function sortNumbers() {
            var ts = Array.prototype.slice(arguments).sort();
            console.log(ts);
        }

        const sortNumbersRest = (...numbers) => console.log(numbers.sort());

        sortNumbers(1, 3, 9, 2);
        sortNumbersRest(1, 3, 9, 2);
        //*********************************************************************************************************//

        //从 ES5 开始，函数内部可以设定为严格模式。
        // ES2016 做了一点修改，规定只要函数参数使用了默认值、解构赋值、或者扩展运算符，
        // 那么函数内部就不能显式设定为严格模式，否则会报错。

        //*********************************************************************************************************//
        //函数name属性，返回函数名
        console.log(foo2.name);//foo2
        var fun111 = function () {//匿名函数
        };
        console.log(fun111.name);//fun111
        var fun222 = function myFun() {

        }
        console.log(fun222.name);//myFun
        //*********************************************************************************************************//
        //箭头函数(谨记以下三条)
        //1.如果箭头函数不需要参数或需要多个参数，就使用一个圆括号代表参数部分。
        //2./如果箭头函数的代码块部分多于一条语句，就要使用大括号将它们括起来，并且使用return语句返回。
        //3.由于大括号被解释为代码块，所以如果箭头函数直接返回一个对象，必须在对象外面加上括号。

        var getTempItem = id => ({id: id, name: "Temp"});
        //这里的{}表示对象，这个对象有两个属性id和name，这里结合前面变量的解构赋值进行理解，
        //这里的()为了防止{}被解释为代码块

        console.log(getTempItem(123456));


        //箭头函数可以与变量解构结合使用。
        const full = ({first, last}) => first + " " + last;
        console.log(full({first: "jiang", last: "li"}));

        const isEven = n => n % 2 == 0;
        const square = n => n * n;
        console.log(isEven(11), square(3));

        var map = [1, 2, 3].map(x => x * x);
        console.log(map);// [ 1, 4, 9 ]

        const numbers = (...nums) => nums;
        console.log(numbers(1, 2, 3, 4, 5));//[ 1, 2, 3, 4, 5 ]

        const headAndTial = (head, ...tail) => [head, tail];
        console.log(headAndTial(1, 2, 3, 4, 5));//[ 1, [ 2, 3, 4, 5 ] ]

        //对于箭头函数，需要注意：
        // 函数体内的this对象，就是定义时所在的对象，绑定定义时所在的定义域。

        //嵌套的箭头函数
        // function insert(value) {
        //     return {
        //         into: function (array) {
        //             return {
        //                 after: function (afterValue) {
        //                     array.splice(array.indexOf(afterValue) + 1, 0, value);
        //                     return array;
        //                 }
        //             };
        //         }
        //     };
        // }
        //
        // insert(2).into([1, 3]).after(1);

        let insert = (value) => (//里面是into函数对象，所以需要（）
            {
                into: (array) => (//里面是array函数对象，所以需要（）
                    {
                        after: (afterValue) => {//里面是after函数体，并且多条语句
                            array.splice(array.indexOf(afterValue) + 1, 0, value);
                            console.log(array);
                            return array;
                        }
                    }
                )
            }
        );
        insert(2).into([1, 3]).after(1);
        //*********************************************************************************************************//
        //管道机制的例子
        //todo 看不懂
        const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val);
        const plus1 = a => a + 1;
        const mult2 = a => a * 2;
        const addThenMult = pipeline(plus1, mult2);
        console.log(addThenMult(5));//12
        //等同于
        console.log(mult2(plus1(5)));//12


        //*********************************************************************************************************//
        //绑定this
        //先了解call apply bind 三个方法调用
        var a = {
            user: "jiangli",
            fun: function (...s) {
                console.log(this.user, s);
            },
        };
        // // var b=a.fun;
        // // b();//undefined
        //
        // // a.fun(1,2,3);//jiangli
        //
        // var b=a.fun;
        // b.call(a,1,2,3);//jiangli  call就是把b添加到a所在的环境中，会将b绑定到a
        //call可以有多个参数

        // var b=a.fun;
        // b.apply(a,[1,2,3]); //jiangli
        // //同样apply也可以有多个参数，但是不同的是，第二个参数必须是一个数组，如下：
        //
        //
        // var b=a.fun;
        // b.bind(a);//什么都没有打印，
        // // //这就是bind和call、apply方法的不同，实际上bind方法返回的是一个修改过后的函数
        // var bind = b.bind(a);
        // bind(1,2,3);//打印出来jiangli

        //*********************************************************************************************************//
        //左边是对象，右边是一个函数，::会将左边的对象绑定到右边的函数上
        //上下文环境:: 函数
        //todo 双冒号理解不了。。。。。
//         foo::bar;
// // 等同于
//         bar.bind(foo);
//
//         foo::bar(...arguments);
// // 等同于
//         bar.apply(foo, arguments);
        //  lambda 中 双冒号是方法的引用，引用格式如下
        // 类名::方法名

    }

    /**
     * 数组的扩展
     */
    arrayTest() {
        //扩展运算符 用...表示，rest参数的逆运算，将数组转化为分隔的参数序列
        console.log(...[1, 2, 3]);//1, 2, 3


        let x = 1;
        let arr = [...(x > 0 ? ['a'] : []), 'b',];//...后面可以放表达式
        console.log(arr);//[ 'a', 'b' ]

        // ES6 的写法
        var arr1 = [0, 1, 2];
        var arr2 = [3, 4, 5];
        arr1.push(...arr2);
        //push函数，将一个数组添加到另一个数组的尾部。
        //push方法的参数不能是数组，
        console.log(arr1);//[ 0, 1, 2, 3, 4, 5 ]
        console.log([...arr1, ...arr2]);//合并数组  [ 0, 1, 2, 3, 4, 5, 3, 4, 5 ]

        //扩展运算符的用处：函数的返回值，对于需要多个返回值的，可返回对象或数组

        //字符串转化为数组 注意：对于实现了Iterator接口类似数组的对象，都可以通过扩展运算符将其转化为数组
        console.log([...'hello']);//[ 'h', 'e', 'l', 'l', 'o' ]

        let map = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
        console.log(...map.keys(), ...map.values());//1, 2, 3, 'one', 'two', 'three'

        //todo 看到Generator 回头再看
        var go = function* () {
            yield 1;
            yield 2;
            yield 3;
        };
        console.log([...go()]); // [1, 2, 3]

        //*********************************************************************************************************//
        //Array.from方法用于将两类对象转为真正的数组：
        // 类似数组的对象（array-like object）和可遍历（iterable）的对象（包括ES6新增的数据结构Set和Map）。
        //所谓类似数组的对象，本质特征只有一点，即必须有length属性。
        //因此，任何有length属性的对象，都可以通过Array.from方法转为数组，而此时扩展运算符就无法转换。
        //只要是部署了Iterator接口的数据结构，Array.from都能将其转为数组。
        let arrayLike = {
            0: 'a',
            1: 'b',
            2: 'c',
            3: 'd',
            length: 4
        };
        console.log(Array.from(arrayLike));//[ 'a', 'b', 'c', 'd' ]
        console.log(Array.from(arrayLike, x => x + "m"));//[ 'am', 'bm', 'cm', 'dm' ]
        //第二个参数相当于对数组进行map操作

        //*********************************************************************************************************//
        // Array.of方法用于将一组值，转换为数组。相当于new Array()
        console.log(Array.of(3));//[3]
        console.log(Array.of(3, 1));//[3,1]
        console.log(Array.of(undefined));//[undefined]
        console.log(Array.of(null));//[ null ]

        //*********************************************************************************************************//
        //copyWithin(target,start,end);
        //将子数组[start,end)，复制到[target,target+(end-start))
        //target（必需）：从该位置开始替换数据。
        //start（可选）：从该位置开始读取数据，默认为0。如果为负值，表示倒数。
        // end（可选）：到该位置前停止读取数据，默认等于数组长度。如果为负值，表示倒数.

        //*********************************************************************************************************//
        // 数组实例的find方法，用于找出第一个符合条件的数组成员。
        // 它的参数是一个回调函数，所有数组成员依次执行该回调函数，直到找出第一个返回值为true的成员，然后返回该成员。
        // 如果没有符合条件的成员，则返回undefined。

        //数组实例的findIndex方法，用于找出第一个符合条件的数组成员的下标。
        // 它的参数是一个回调函数，所有数组成员依次执行该回调函数，直到找出第一个返回值为true的成员，然后返回该成员的下标。
        // 如果没有符合条件的成员，则返回-1。

        //*********************************************************************************************************//
        //数组实例的fill方法
        console.log(new Array(3).fill(7));// [ 7, 7, 7 ]
        console.log(["a","b","c"].fill(7,1,2));//[ 'a', 7, 'c' ]
        //接受第二个和第三个参数，用于指定填充的起始位置和结束位置[ )。

        //*********************************************************************************************************//
        //数组实例的entries()，keys()和values()——用于遍历数组。
        //可以用for...of循环进行遍历，
        //区别是keys()是对键名的遍历、values()是对键值的遍历，entries()是对键值对的遍历。
        var array = Array.of('a','c','e');
        for(let index of array.keys()){
            console.log(index);
        }
        for(let elem of array.values()){
            console.log(elem);
        }
        for(let [index, value] of array.entries()){
            console.log(index,value);
        }
        //*********************************************************************************************************//
        //数组实例的includes（）
        //方法返回一个布尔值，表示某个数组是否包含给定的值，与字符串的includes方法类似
        // 该方法的第二个参数表示搜索的起始位置，默认为0。
        // 如果第二个参数为负数，则表示倒数的位置，如果这时它大于数组长度（比如第二个参数为-4，但数组长度为3），则会重置为从0开始。
        //不管起始位置是正负，搜索的方向都是从前到后的！！！
        console.log([1,2,3].includes(3,-4));//true
        console.log([1,2,3].includes(2,-2));//true 搜索的数字：1,2
        console.log([1,2,3,4,5].includes(3,-1));//false  搜索的数字：4,5

        //*********************************************************************************************************//
        //ES6 则是明确将空位转为undefined。
        //由于空位的处理规则非常不统一，所以建议避免出现空位。
        console.log(Array.from(['a',,'b']));// [ 'a', undefined, 'b' ]

    }

    /**
     * 对象的扩展
     *
     */
    objectTest(){

        //*********************************************************************************************************//
    }

    render() {
        return (
            <ScrollView>
                <View>
                    <Button onPress={() => {
                        this.letTest()
                    }}
                            title='let和const命令'/>
                    <Button onPress={() => {
                        this.destructionTest()
                    }}
                            title='变量的解构赋值'
                            color='#841584'/>

                    <Button onPress={() => {
                        this.stringTest();
                    }}
                            title='字符串的扩展'/>
                    <Button onPress={() => {
                        this.regularTest();
                    }}
                            title='正则的扩展'
                            color='#841584'
                    />
                    <Button onPress={() => {
                        this.numberTest();
                    }}
                            title='数值的扩展'
                    />
                    <Button onPress={() => {
                        this.funcTest();
                    }}
                            title='函数的扩展'
                            color='#841584'
                    />
                    <Button onPress={() => {
                        this.arrayTest();
                    }}
                            title='数组的扩展'
                    />
                    <Button onPress={() => {
                        this.objectTest();
                    }}
                            title='对象的扩展'
                            color='#841584'
                    />
                </View>
            </ScrollView>
        );
    }


}